package com.gitee.huanminabc.utils_tools.web_base.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.gitee.huanminabc.utils_tools.web_base.utils.RequestWrapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.function.Function;

/**
 * 需要调用方自己配置拦截器
 */
@Slf4j
@Component("requestLogTimeInterceptor")
public class RequestLogTimeInterceptor implements HandlerInterceptor {

    @Value("${spring.application.authorizationName:'Authorization'}")
    private String authorizationName;

    public static Function<HttpServletRequest, String> getUserNameFun = (request) -> "unknown";

    //请求处理前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("StartTime", System.currentTimeMillis());
        //判断请求头是否携带token,其他请求不重要,不记录
        String url = request.getRequestURI();
        String token = request.getHeader(authorizationName);
        if (token != null && handler instanceof HandlerMethod) {
            RequestParam requestParam = new RequestParam();
            requestParam.setMethod(request.getMethod());
            requestParam.setScheme(request.getScheme());
            requestParam.setIp(request.getRemoteAddr());
            requestParam.setPort(String.valueOf(request.getRemotePort()));
            requestParam.setUrl(url);
            requestParam.setParamString(getParamString(request));
            requestParam.setFormString(getFormString(request));
            requestParam.setBodyString(getBodyString(request));
            requestParam.setAccount(getUserNameFun.apply(request));
            requestParam.setTarget(((HandlerMethod) handler).getMethod().getDeclaringClass() + "." + ((HandlerMethod) handler).getMethod().getName());
            log.info("{}请求参数信息:{}", url, JSONObject.toJSONString(requestParam, SerializerFeature.PrettyFormat));
        }
        return true;
    }

    //请求处理后
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    //请求处理完毕 ,这个和postHandle有点类似,但是这个是在整个请求处理完毕后调用的,也就是在DispatcherServlet渲染了对应的视图之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        Long start = (Long) request.getAttribute("StartTime");
        String url = request.getRequestURI();
        long time = System.currentTimeMillis() - start;
        log.info("{},请求执行完毕总耗时:{}ms", url, time);
    }

    //获取get请求参数
    private String getParamString(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        request.getParameterMap().forEach((k, v) -> {
            sb.append(k).append("=").append(v[0]).append("&");
        });
        return sb.toString();
    }

    //获取form表单请求参数,转换为json
    private String getFormString(HttpServletRequest request) {
        JSONObject jsonObject = new JSONObject();
        request.getParameterMap().forEach((k, v) -> {
            jsonObject.put(k, v[0]);
        });
        return jsonObject.toString();
    }

    //获取请求体
    @SneakyThrows
    private String getBodyString(HttpServletRequest request) {
        //判断类型是RequestWrapper
        if (request instanceof RequestWrapper) {
            return ((RequestWrapper) request).getBody().toString();
        }
        return new RequestWrapper(request).getBody().toString();
    }

    @Data
    public static class RequestParam {
        private String method; //请求类型
        private String scheme; //请求协议
        private String ip; //请求ip
        private String port; //请求端口
        private String url; //请求路径
        private String paramString; //get请求参数
        private String formString; //form表单请求参数
        private String bodyString; //请求体
        private String account; //用户
        private String target; //类到方法的路径
    }
}
